package com.lyEducation.util.Socket.pointSend;

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class PointIncepter {
  // 断点不存在或出现错误则返回backup
  private static int GetBreakPoint(String bpFile, int backup) {
    FileReader fr = null;
    try {
      fr = new FileReader(bpFile);
      BufferedReader br = new BufferedReader(fr);
      File file = new File(bpFile);
      System.out.println(file.getAbsolutePath());
      String numStr = br.readLine();
      return Integer.parseInt(numStr);
    } catch (NumberFormatException | IOException e) {
      if (e instanceof FileNotFoundException) {
        try {
          SetBreakPoint(bpFile, 0);
        } catch (FileNotFoundException e1) {
          // TODO Auto-generated catch block
          e1.printStackTrace();
        }
      }
      return backup;
    } finally {
      try {
        if (fr != null) {
          fr.close();
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  private static void SetBreakPoint(String bpFile, int num) throws FileNotFoundException {
    File file = new File(bpFile);
    if (!file.exists())
      try {
        file.createNewFile();
        System.out.println(file.getAbsolutePath());
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    PrintStream f = new PrintStream(bpFile);
    f.println(num);
    f.close();
  }

  private static void DelBreakPoint(String bpFile) {
    File f = new File(bpFile);
    if (f.exists()) {
      f.delete();
    }
  }

  private static Boolean CheckMD5(byte[] data, int start, byte[] md5) {
    MessageDigest md;
    try {
      md = MessageDigest.getInstance("MD5");
      if (start == 0) {
        md.update(data);
      } else {
        byte[] part = new byte[data.length - start];
        System.arraycopy(data, start, part, 0, part.length);
        md.update(part);
      }

    } catch (NoSuchAlgorithmException e) {
      return false;
    }
    byte[] result = md.digest();
    for (int i = 0; i < result.length; ++i) {
      if (md5[i] != result[i]) {
        return false;
      }
    }
    return true;
  }

  public static void main(String[] args) {
    System.out.println(Class.class.getClass().getResource("/").getPath());
    new PointIncepter()
        .receiveFile(
            "C:\\Users\\WSF\\Desktop\\a.txt",
            "localhost",
            8821,
            "C:\\Users\\WSF\\Desktop\\test.txt");
  }

  void receiveFile(String srcFile, String address, int port, String dstFile) {

    String[] pathSplit = srcFile.trim().split("\\\\");
    String bpFile = pathSplit[pathSplit.length - 1] + ".index";
    int begin = GetBreakPoint(bpFile, 0);
    Socket client;
    try {
      client = new Socket(address, port);
      OutputStream out = client.getOutputStream();
      InputStream in = client.getInputStream();
      final int bufLen = 8 * 1000 * 1000; // 8M
      byte[] buf = new byte[4 + 16 + bufLen]; // 4位有效数据长度(int)，16位md5值

      for (int piece = begin; ; ++piece) {
        SetBreakPoint(bpFile, piece);

        // 向Server发送指令
        String order = String.format("%s|%d|%d", srcFile, piece, bufLen);
        out.write(order.getBytes());
        // 接收数据
        int sum = 0;
        int complete;
        while ((complete = in.read(buf, sum, 20 + bufLen - sum)) > 0 && sum < 20 + bufLen) {
          sum += complete;
        }
        // 分析数据，判断文件结束，校验md5
        // 读取有效数据长度
        byte[] realBufLenByte = new byte[4];
        System.arraycopy(buf, 0, realBufLenByte, 0, 4);
        int realBufLen = ByteBuffer.wrap(realBufLenByte).getInt();
        // 文件到头了
        if (realBufLen <= 0) {
          break;
        }

        // 检查md5值的正确性
        byte[] md5 = new byte[16];
        System.arraycopy(buf, 4, md5, 0, 16);
        if (!CheckMD5(buf, 20, md5)) {
          // 错误则重试
          --piece;
          continue;
        }

        // 将数据写入文件
        FileOutputStream fw = new FileOutputStream(dstFile, true); // append=true
        fw.write(buf, 20, realBufLen);
        fw.close();
      }
      DelBreakPoint(bpFile);
      // 关闭连接
    } catch (UnknownHostException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}
